﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Log4Nez.Interfaces;
using System.Diagnostics;
using System.Reflection;

namespace Log4Nez.Loggers
{
    internal class EventLogLogger : ILogger
    {
        private EventSourceCreationData _eventSource = null;
        private string _logEventName = string.Empty;

        private string _originIdProp = string.Empty;
        private string _eventIdProp = string.Empty;
        private string _categoryIdProp = string.Empty;
        private string _instanceIdProp = string.Empty;
        private string _messageProp = string.Empty;

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="source"></param>
        /// <param name="logname"></param>
        /// <param name="originIdProp"></param>
        /// <param name="eventIdProp"></param>
        /// <param name="categoryIdProp"></param>
        /// <param name="instanceIdProp"></param>
        /// <param name="messageProp"></param>
        public EventLogLogger(string source, string logname, string originIdProp, string eventIdProp,
            string categoryIdProp, string instanceIdProp, string messageProp)
        {
            this._eventSource = new EventSourceCreationData(source, logname);
            this._logEventName = logname;
            this._originIdProp = originIdProp;
            this._eventIdProp = eventIdProp;
            this._categoryIdProp = categoryIdProp;
            this._instanceIdProp = instanceIdProp;
            this._messageProp = messageProp;            
        }

        /// <summary>
        /// Writes a message in the log
        /// </summary>
        /// <param name="level">level of </param>
        /// <param name="message"></param>
        /// <returns></returns>
        public bool Write(System.Diagnostics.TraceLevel level, object message, params string[] formatParams)
        {
            try
            {
                if (!EventLog.Exists(_logEventName))
                    EventLog.CreateEventSource(_eventSource);

                PropertyInfo[] properties = message.GetType().GetProperties();
                long instanceId = 0;
                long.TryParse(properties.FirstOrDefault(p => p.Name == _instanceIdProp).GetValue(message, null).ToString(), out instanceId);

                int categoryId = 0;
                int.TryParse(properties.FirstOrDefault(p => p.Name == _categoryIdProp).GetValue(message, null).ToString(), out categoryId);

                EventLogEntryType eventLogEntryType = convertEventLog(level);

                EventInstance inst = new EventInstance(instanceId, categoryId, eventLogEntryType);
                EventLog.WriteEntry(properties.FirstOrDefault(p => p.Name == _originIdProp).GetValue(message, null).ToString(),
                    properties.FirstOrDefault(p => p.Name == _messageProp).GetValue(message, null).ToString(),
                    eventLogEntryType,
                    (int)properties.FirstOrDefault(p => p.Name == _eventIdProp).GetValue(message, null),
                    (short)properties.FirstOrDefault(p => p.Name == _categoryIdProp).GetValue(message, null));
                    EventLog.WriteEvent(properties.FirstOrDefault(p => p.Name == _originIdProp).GetValue(message, null).ToString(), 
                    inst, 
                    properties.FirstOrDefault(p => p.Name == _messageProp).GetValue(message, null).ToString());

                return true;
            }
            catch (Exception)
            {
                return false;
            }
            
        }

        private EventLogEntryType convertEventLog(TraceLevel level)
        {
            switch (level)
            {
                case TraceLevel.Error:
                    return EventLogEntryType.Error;
                case TraceLevel.Verbose:
                case TraceLevel.Info:
                    return EventLogEntryType.Information;
                case TraceLevel.Off:
                    return EventLogEntryType.Error;
                case TraceLevel.Warning:
                    return EventLogEntryType.Warning;
            }
            return EventLogEntryType.Error;

        }

        public TraceLevel LoggerLevel
        {
            get;
            set;
        }
    }
}
